/***************************************************************************
 *   File Buffer.cpp		author belot nicolas (karrakis)            *
 *   define Class Buffer                                                   *
 *   a basic buffer storing array of double                                *
 ***************************************************************************
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "Buffer.h"
#include <string.h>
#include <iostream>
#include <cassert>

namespace libtrckr {
Buffer::Buffer( ):_size(SIZE),_position(0),_channels(1),_current(0){
	_buffer=(double*)malloc(sizeof(double)*SIZE);
}

Buffer::Buffer(int size):_size(size),_position(0),_channels(1),_current(0){
	_buffer=(double*)malloc(sizeof(double)*_size);
	//feed with 0!!
	memset(_buffer,0,sizeof(double)*_size);
	/*for(unsigned int i=0;i<_size;i++){
		_buffer[i]=0.0;
	}*/
}

Buffer::Buffer(double* data,int size):_size(size),_position(size),_channels(1),_current(0){
	_buffer=(double*)malloc(sizeof(double)*_size);
	memcpy(_buffer,data,sizeof(double)*_size);
}

Buffer::Buffer(double* data, int position,int size):_size(size),_position(position),_channels(1),_current(0){
	//allocate Buffer
	_buffer=(double*)malloc(sizeof(double)*_size);
	memcpy(_buffer,data,sizeof(double)*_position);
	memset(_buffer+_position, 0, sizeof(double)*(_size-_position));
	/*for(unsigned int i=_position;i<_size;i++){
		_buffer[i]=0.0;
	}*/

	
}

Buffer::Buffer(const Buffer& b):_size(b._size),_position(b._position),_channels(b._channels),_current(0){
	_buffer=(double*)malloc(sizeof(double)*_size*b._channels);
	memcpy(_buffer,b._buffer, sizeof(double)*_size*b._channels);
}

Buffer::~Buffer(){
	free(_buffer);
}

size_t Buffer::size() const {
	return _size;
}

size_t Buffer::capacity() const{
	return _size;
}

double* Buffer::getContent() const {
	return _buffer;
}

bool Buffer::full() const{
	return _size==_position;
}

size_t Buffer::position() const{
	return _position;
}

size_t Buffer::end() const {
	return _position;
}

void Buffer::clearChannel(){
	for(uint i=0;i<_size;i++){
		_buffer[(i*_channels)+_current]=0.;
	}
	_position=0;
}

void Buffer::flush(){
	memset(_buffer,0, sizeof(double)*_size*_channels);
	/*for(unsigned int i=0;i<_size;i++){
		_buffer[i]=0.0;
	}*/
	_position=0;
}

void Buffer::channels(int Channels){
	if(_channels!=Channels){
		double* tmp=(double*)realloc(_buffer,sizeof(double)*_size*Channels);
		if(tmp==NULL) exit(0);
		int old=_channels;
		_channels=Channels;
		_buffer=tmp;
		for(int i=(old-1);i>=0;i--){
			for(int j=(_size-1);j>=0;j--){
				tmp[j*_channels+i]=tmp[j*old+i];
				tmp[j*old+i]=0.;
			}
		}

	}
}

int Buffer::channels() const {
	return _channels;
}

void Buffer::setChannel(int chan){
	if(chan<_channels){
		_current=chan;
	}
}

int Buffer::getChannel()const{
	return _current;
}

bool Buffer::hasNextChannel(){
	return _current < _channels;
}

void Buffer::nextChannel(){
	_current++;
}

double Buffer::operator[](unsigned int item) const{
	if(item > _size )
		throw new std::out_of_range("Try to access out of range elemen");
	
	return _buffer[(item*_channels)+_current];
	
}

double& Buffer::operator[](unsigned int item){
	
	if(item > _size){
		std::cerr << "Item : " << item << std::endl;
		throw std::out_of_range("Try to access out of range element ");
		
	}

	if(item>_position)
		_position=item;

	return _buffer[(item*_channels)+_current];
}

Buffer Buffer::operator=(const Buffer& b){
	_size=b._size;
	_position=b._position;
	_channels=b._channels;
	_current=0;
	_buffer=(double*)malloc(sizeof(double)*_size*b._channels);
	memcpy(_buffer,b._buffer, sizeof(double)*_size*b._channels);
	return *this;
}

void Buffer::append(double*data,  unsigned int size){
	append(data,size,1);
}

void Buffer::append(double* data, unsigned int size, int channels){
	if(_position + size > _size)
		throw std::out_of_range("Try to access out of range element");

	unsigned int end=_position +size;
	memcpy(_buffer+_position, data, sizeof(double)*size*channels);
	//for(unsigned int i=_position;i<end;i++){
	//	_buffer[i]=data[i-_position];
	//}
	_position=end;
}

void Buffer::append(const Buffer& t){
	if(_position + t._size > _size){
		std::cout << _position + t._size << " ," << t._size << ", " << _size << std::endl;
		throw std::out_of_range("Try to access out of range element");
	}
	if(t._channels==_channels){
		unsigned int end=_position + t._size;
		memcpy(_buffer+_position,t._buffer,t._size*t._channels);
		/*for(unsigned int i=_position;i<end;i++){
			_buffer[i]=t._buffer[i-_position];
		}*/
		_position=end;
	}
}

Buffer Buffer::operator+(const Buffer& b) const{
	int si = _size + b._size;
	Buffer res=Buffer(_buffer,_position,si);
	res.append(b);
	return res;
}

void Buffer::operator+=(const Buffer&b){
	append(b);
}

void Buffer::operator<<(const Buffer&b){
	append(b);
}
}

